package fr.babos.graphic.components.bitmaps 
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.PixelSnapping;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import fr.babos.graphic.components.base.ResizableComponent;
	
	/**
	 * @author ZoulouX
	 */
	public class AutoScaleNineBitmaps extends ResizableComponent 
	{
		/**
		 * Le BitmapData source non traité
		 */
		protected var _bitmapData				:BitmapData;
		
		/**
		 * Les coordonnées des tranches a suivre pour le découpage
		 */
		protected var _slices					:Rectangle;
		
		/**
		 * Si le bitmapData est coupé automatiquement (donc il faut enlever 2px en haut et en bas)
		 */
		protected var _autoSliced				:Boolean;
		
		/**
		 * Le nombre de tranches horizontales
		 */
		protected var _horizontalSlices			:uint;
		
		/**
		 * Le nombre de tranches verticales
		 */
		protected var _verticalSlices			:uint;
		
		/**
		 * La couleur limite pour le découpage
		 */
		protected var _cutThreshold				:uint							= 0x777777;
		
		/**
		 * Si on doit lisser l'image
		 */
		protected var _smoothing				:Boolean						= true;
		
		/**
		 * Les bitmaps (1 par coupe)
		 */
		protected var _bitmaps					:Vector.<Vector.<Bitmap>>;
		
		/**
		 * La densité de l'image
		 */
		protected var _density					:Number							= 1;
		
		
		/**
		 * Le BitmapData source non traité
		 */
		public function get bitmapData ():BitmapData { return _bitmapData; }
		
		/**
		 * Les coordonnées des tranches a suivre pour le découpage
		 */
		public function get slices ():Rectangle { return _slices; }
		
		/**
		 * Si le bitmapData est coupé automatiquement (donc il faut enlever 2px en haut et en bas)
		 */
		public function get autoSliced ():Boolean { return _autoSliced; }
		
		/**
		 * Le nombre de tranches horizontales
		 */
		public function get horizontalSlices ():uint { return _horizontalSlices; }
		
		/**
		 * Le nombre de tranches verticales
		 */
		public function get verticalSlices ():uint { return _verticalSlices; }
		
		/**
		 * La couleur limite pour le découpage
		 */
		public function get cutThreshold ():uint { return _cutThreshold; }
		public function set cutThreshold (value:uint):void
		{
			_cutThreshold = value;
		}
		
		/**
		 * Si on doit lisser l'image
		 */
		public function get smoothing ():Boolean { return _smoothing; }
		public function set smoothing (value:Boolean):void 
		{
			_smoothing = value;
		}
		
		/**
		 * La densité de l'image
		 */
		public function get density ():Number { return _density; }
		
		
		/**
		 * Constructeur du coupeur de bitmaps automatique
		 * @param	pBitmapData : Le BitmapData a couper
		 * @param	pDensity : La densité de l'image
		 * @param	pSlices : Le rectangle contenant les coordonnées des coupures (null pour automatique)
		 */
		public function AutoScaleNineBitmaps (pBitmapData:BitmapData, pDensity:Number = 1, pSlices:Rectangle = null)
		{
			setBitmapData(pBitmapData, pDensity, pSlices);
		}
		
		/**
		 * Définir la source
		 * @param	pBitmapData : Le BitmapData a couper
		 * @param	pSlices : Le rectangle contenant les coordonnées des coupures (null pour automatique)
		 */
		public function setBitmapData (pBitmapData:BitmapData, pDensity:Number = 1, pSlices:Rectangle = null):void
		{
			// Enregistrer l'image
			_bitmapData = pBitmapData;
			
			// Enregistrer la densité
			_density = pDensity;
			
			// Si on n'a pas de coordonnées de coupure
			if (pSlices == null)
			{
				// Récupérer automatiquement les coupures depuis l'image source
				getSlices();
				
				// On est en mode auto
				_autoSliced = true;
			}
			else
			{
				// On enregistre les coordonnées
				_slices = pSlices;
				
				// Vérifier le nombre de blocs qu'on a horizontalement et verticalement/
				_horizontalSlices = (_slices.left < 0 || _slices.width < 0 || _slices.right < 0) ? 1 : 3;
				_verticalSlices = (_slices.top < 0 || _slices.height < 0 || _slices.bottom < 0) ? 1 : 3;
				
				// On n'est pas en mode auto
				_autoSliced = false;
			}
			
			// Supprimer les anciens (si besoin)
			deleteSlices();
			
			// Couper le bitmap
			slice();
			
			// Actualiser l'affichage
			draw();
		}
		
		/**
		 * Récupérer la position des coupures sur l'image
		 */
		protected function getSlices ():void
		{
			// Les limites
			var x1:uint = 1;
			var x2:uint = _bitmapData.width - 1;
			var y1:uint = 1;
			var y2:uint = _bitmapData.height - 1;
			
			// Récupérer le scale horizontal de gauche
			while (_bitmapData.getPixel(x1, 0) > _cutThreshold && x1 < _bitmapData.width)
			{
				// On va vers la droite
				x1 ++;
			}
			
			// Vérifier si on a trouvé un truc
			if (x1 < x2)
			{
				// On a 3 blocs horizontaux
				_horizontalSlices = 3;
				
				// Récupérer le scale horizontal de droite
				while (_bitmapData.getPixel(x2, 0) > _cutThreshold && x2 > 1)
				{
					// On va vers la gauche
					x2 --;
				}
			}
			else
			{
				// On a qu'un seul bloc horizontal
				_horizontalSlices = 1;
				
				// Remettre le premier à la fin
				x1 = x2;
			}
			
			// Récupérer le scale vertical du haut
			while (_bitmapData.getPixel(0, y1) > _cutThreshold && y1 < _bitmapData.height)
			{
				// On va vers le bas
				y1 ++;
			}
			
			// Vérifier si on a trouvé un truc
			if (y1 < y2)
			{
				// On a 3 blocs verticaux
				_verticalSlices = 3;
				
				// Récupérer le scale vertical du haut
				while (_bitmapData.getPixel(0, y2) > _cutThreshold && y2 > 1)
				{
					// On va vers le haut
					y2 --;
				}
			}
			else
			{
				// On a qu'un seul bloc vertical
				_verticalSlices = 1;
				
				// Remettre le premier à la fin
				y1 = y2;
			}
			
			// Enregistrer le rectangle
			_slices = new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
		}
		
		/**
		 * Effacer les anciennes coupes
		 */
		protected function deleteSlices ():void
		{
			if (_bitmaps != null)
			{
				// Parcourir les blocs horizontalement
				for (var cx:uint = 0; cx < _bitmaps.length; cx++)
				{
					// Parcourir les blocs verticalement
					for (var cy:uint = 0; cy < _bitmaps[cx].length; cy++)
					{
						// Disposer le bitmapData
						_bitmaps[cx][cy].bitmapData.dispose();
						
						// Supprimer le bloc de la DisplayList
						removeChild(_bitmaps[cx][cy]);
					}
				}
			}
			
			// Réinitialiser les bitmaps
			_bitmaps = new Vector.<Vector.<Bitmap>>;
		}
		
		/**
		 * Couper
		 */
		protected function slice ():void
		{
			// Si on a une image source
			if (_bitmapData != null && _slices != null)
			{
				// Les dimensions de l'image avec ou sans les coupures
				const imageOffset:uint = (_autoSliced ? 2 : 0);
				
				// Les positions des lignes / colonnes
				var rows	:Vector.<Number> = Vector.<Number>([imageOffset, _slices.top, _slices.bottom, _bitmapData.height]);
				var cols	:Vector.<Number> = Vector.<Number>([imageOffset, _slices.left, _slices.right, _bitmapData.width]);
				
				// Les rectangles de placement
				var origin	:Rectangle;
				
				// Le bitmap et ses données qui vont être créés
				var currentBitmapData	:BitmapData;
				var currentBitmap		:Bitmap;
				
				// Le point pour la copie (origine)
				var topPoint			:Point = new Point(0, 0);
				
				// Parcourir les blocs horizontalement
				for (var cx:uint = 0; cx < _horizontalSlices; cx++)
				{
					// Créer la seconde dimension du vecteur
					_bitmaps[cx] = new Vector.<Bitmap>;
					
					// Parcourir les blocs verticalement
					for (var cy:uint = 0; cy < _verticalSlices; cy++)
					{
						// Cibler l'origine de la coupe
						origin = new Rectangle(cols[cx], rows[cy], cols[cx + 1] - cols[cx], rows[cy + 1] - rows[cy]);
						
						// Créer le bitmapData de ce bloc
						currentBitmapData = new BitmapData(origin.width, origin.height, _bitmapData.transparent);
						
						// Copier les pixels du bitmap d'origine vers le bitmap du bloc
						currentBitmapData.copyPixels(_bitmapData, origin, topPoint);
						
						// Créer le bloc et lui associer le bitmapData
						currentBitmap = new Bitmap(currentBitmapData, _smoothing ? PixelSnapping.AUTO : PixelSnapping.NEVER, _smoothing);
						
						// Enregistrer ce bitmap dans le vecteur bi-dimensionnel
						_bitmaps[cx][cy] = currentBitmap;
						
						// L'ajouter à la scène
						addChild(_bitmaps[cx][cy]);
					}
				}
			}
		}
		
		/**
		 * Le composant est redimensionné
		 */
		override protected function resized ():void
		{
			draw();
		}
		
		/**
		 * Actualiser l'affichage
		 */
		public function draw ():void
		{
			// Si on a une image source
			if (_bitmapData != null && _slices != null)
			{
				// Les dimensions de l'image avec ou sans les coupures
				const imageOffset:uint = (_autoSliced ? 2 : 0);
				
				// Les positions des lignes / colonnes
				var dRows	:Vector.<Number> = Vector.<Number>([
					0,
					_slices.top - imageOffset,
					(_localHeight * _density - (_bitmapData.height - _slices.bottom)),
					_localHeight * _density
				]);
				var dCols	:Vector.<Number> = Vector.<Number>([
					0,
					_slices.left - imageOffset,
					(_localWidth * _density - (_bitmapData.width - _slices.right)),
					_localWidth * _density
				]);
				
				// Le bitmap traité
				var currentBitmap:Bitmap;
				
				// Parcourir les blocs horizontalement
				for (var cx:uint = 0; cx < _horizontalSlices; cx++)
				{
					// Parcourir les blocs verticalement
					for (var cy:uint = 0; cy < _verticalSlices; cy++)
					{
						// Cibler le bitmap
						currentBitmap = _bitmaps[cx][cy];
						
						// Placer le bloc
						currentBitmap.x = int(dCols[cx] / _density + .5);
						currentBitmap.y = int(dRows[cy] / _density + .5);
						currentBitmap.width = int((dCols[cx + 1] - dCols[cx]) / _density + .5);
						currentBitmap.height = int((dRows[cy + 1] - dRows[cy]) / _density + .5);
						/*
						currentBitmap.x = dCols[cx] / _density;
						currentBitmap.y = dRows[cy] / _density;
						currentBitmap.width = (dCols[cx + 1] - dCols[cx]) / _density;
						currentBitmap.height = (dRows[cy + 1] - dRows[cy]) / _density;
						
						trace("SCALE9", cx, cy, currentBitmap.width, currentBitmap.bitmapData.width);
						*/
					}
				}
			}
		}
		
		/**
		 * Destruction
		 */
		override public function dispose ():void
		{
			deleteSlices();
			
			_bitmapData = null;
			_slices = null;
			
			super.dispose();
		}
	}
}